package redisSync

import (
	"github.com/garyburd/redigo/redis"
	"log"
	"os"
	// "reflect"
	// "hub"
	// "strconv"
	"bufio"
	"time"
)

type Hub struct {
	srcPool    *redis.Pool
	destPool   *redis.Pool
	keysChan   chan []interface{}
	keyChan    chan string
	stringChan chan dataObj
	hashChan   chan dataObj
	listChan   chan dataObj
	setChan    chan dataObj
	zsetChan   chan dataObj
	count      int
	// importChan chan map[string]string
}

type dataObj struct {
	key  string
	typ  interface{}
	val  interface{}
	ttl  int64
	conn redis.Conn
}

// type Stat struct {
// 	count int
// 	str   int
// 	hash  int
// 	list  int
// 	set   int
// 	zset  int
// }

func init() {
	h := newHub()
	go h.run()
	listAllItems(h)
}

func Test() {
	log.Println("Test func")
}

func listAllItems(h *Hub) {
	Conn := h.srcPool.Get()
	defer Conn.Close()

	cursor := "0"
	for {
		result, _ := Conn.Do("SCAN", cursor, "COUNT", 50000)

		scanResults, _ := result.([]interface{})
		if len(scanResults) != 2 {
			log.Fatal("scanResults is wrong")
		}
		pos, _ := scanResults[0].([]byte)
		cursor = string(pos)
		keys, _ := scanResults[1].([]interface{})

		// go pushKeys(h, keys)
		go saveKeys(h, keys)

		log.Println("cursor", cursor)
		log.Println("keys len", len(keys))

		if cursor == "0" {
			break
			log.Println("is End")
		}
	}
	for {
		select {
		case <-h.keyChan:

		}
	}
}

func saveKeys(h *Hub, keys []interface{}) {
	log.Println("savekeys")
	// Conn := h.srcPool.Get()
	// destConn := h.destPool.Get()
	var filename = "./output1.txt"
	var f *os.File
	f, _ = os.OpenFile(filename, os.O_APPEND, 0666)
	w := bufio.NewWriter(f) //创建新的 Writer 对象
	for _, key := range keys {
		keyStr := string(key.([]byte))
		w.WriteString(keyStr + "\n")
	}
	w.Flush()
	f.Close()
}

func pushKeys(h *Hub, keys []interface{}) {
	// log.Println(h)
	Conn := h.srcPool.Get()
	destConn := h.destPool.Get()
	// defer log.Println("pushkeys close")
	// defer Conn.Put()
	// defer destConn.Put()
	for _, key := range keys {
		keyStr := string(key.([]byte))
		keyType, _ := Conn.Do("TYPE", keyStr)

		// if keyType == "string" {
		// 	log.Println(keyType, keyStr)
		// }

		switch keyType {
		case "string":
			keyVal, _ := Conn.Do("GET", keyStr)
			keyTtl, _ := Conn.Do("TTL", keyStr)
			h.stringChan <- dataObj{keyStr, keyType, keyVal, keyTtl.(int64), destConn}
		case "hash":
			keyVal, _ := Conn.Do("HGETALL", keyStr)
			keyTtl, _ := Conn.Do("TTL", keyStr)
			h.hashChan <- dataObj{keyStr, keyType, keyVal, keyTtl.(int64), destConn}
		case "list":
			keyVal, _ := Conn.Do("LRANGE", keyStr, 0, -1)
			keyTtl, _ := Conn.Do("TTL", keyStr)
			h.listChan <- dataObj{keyStr, keyType, keyVal, keyTtl.(int64), destConn}
		case "set":
			keyVal, _ := Conn.Do("SMEMBERS", keyStr)
			keyTtl, _ := Conn.Do("TTL", keyStr)
			// log.Println(keyType, keyVal, keyTtl)
			h.setChan <- dataObj{keyStr, keyType, keyVal, keyTtl.(int64), destConn}
		case "zset":
			keyVal, _ := Conn.Do("ZRANGE", keyStr, 0, -1, "WITHSCORES")
			keyTtl, _ := Conn.Do("TTL", keyStr)
			h.zsetChan <- dataObj{keyStr, keyType, keyVal, keyTtl.(int64), destConn}
		default:
			log.Println("type is wrong", keyType)
		}
	}
}

// get db size
func dbSize(h *Hub) int64 {
	Conn := h.srcPool.Get()
	defer Conn.Close()
	size, _ := Conn.Do("DBSIZE")
	return size.(int64)
}

// func checkArgs() {
// 	log.Println(os.Args[0])
// }

func newHub() *Hub {
	return &Hub{
		keysChan:   make(chan []interface{}),
		keyChan:    make(chan string),
		stringChan: make(chan dataObj), // handle string
		hashChan:   make(chan dataObj), // handle hash
		listChan:   make(chan dataObj), // handle liste
		setChan:    make(chan dataObj), // handle set
		zsetChan:   make(chan dataObj), // handle zset
		// 测试业务
		// srcPool:  newRedisPool("10.127.131.192:6379", "L2H8765!%&Kjds", 0),
		// destPool: newRedisPool("10.127.131.192:6380", "L2H8765!%&Kjds", 0),
		// destPool: newRedisPool("10.128.36.122:6379", "e35sioAWR#TG", 0),

		// 业务库
		// srcPool:    newRedisPool("10.127.2.171:6379", "L2H8765!%&Kjds", 0),
		// destPool:   newRedisPool("10.128.36.122:6379", "e35sioAWR#TG", 0),

		// 缓存库
		// srcPool:  newRedisPool("10.127.2.171:6379", "L2H8765!%&Kjds", 1),
		// destPool: newRedisPool("10.128.36.110:6379", "e35sioAWR#TG", 0),

		// 众筹库
		// srcPool:  newRedisPool("10.127.2.171:6381", "L2H8765!%&Kjds", 0),
		// destPool: newRedisPool("10.128.36.75:6379", "e35sioAWR#TG", 0),

		srcPool: newRedisPool("10.128.36.122:6379", "e35sioAWR#TG", 0),

		count: 0,
	}
}

func (h *Hub) run() {
	for {
		select {
		case data := <-h.stringChan:
			// log.Println(data.typ, data.key)
			if data.ttl == -1 {
				_, ok := data.conn.Do("SET", data.key, data.val)
				if ok != nil {
					log.Fatal("SET is wrong", ok)
				}
			} else {
				_, ok := data.conn.Do("SETEX", data.key, data.ttl, data.val)
				if ok != nil {
					log.Fatal("setex is wrong", ok, data.key)
				}
			}
			h.count++
			log.Println("count:", h.count, data.typ, data.key)
		case data := <-h.hashChan:
			// log.Println(data.typ, data.key)
			// go func(h *Hub, data dataObj) {
			dataMap := data.val.([]interface{})
			args := []interface{}{data.key}
			for key, val := range dataMap {
				if key%2 == 0 {
					args = append(args, string(val.([]byte)), string(dataMap[key+1].([]byte)))
				}
			}
			_, ok := data.conn.Do("HMSET", args...)
			if ok != nil {
				log.Fatal("HMSET is wrong", ok)
			}
			if data.ttl != -1 {
				data.conn.Do("EXPIRE", data.key, data.ttl)
			}
			h.count++
			log.Println("count:", h.count, data.typ, data.key)
		// }(h, data)
		case data := <-h.listChan:
			// log.Println(data.typ, data.key)
			// go func(h *Hub, data dataObj) {
			dataMap := data.val.([]interface{})
			list := []interface{}{}
			for _, val := range dataMap {
				list = append(list, string(val.([]byte)))
			}
			args := append([]interface{}{data.key}, list...)
			_, ok := data.conn.Do("RPUSH", args...)
			if ok != nil {
				log.Fatal("SADD is wrong.", ok)
			}
			if data.ttl != -1 {
				data.conn.Do("EXPIRE", data.key, data.ttl)
			}
			h.count++
			log.Println("count:", h.count, data.typ, data.key)
		// }(h, data)
		case data := <-h.setChan:
			// log.Println(data.typ, data.key)
			// go func(h *Hub, data dataObj) {
			members := data.val.([]interface{})
			args := []interface{}{data.key}
			for _, m := range members {
				args = append(args, string(m.([]byte)))
			}
			// log.Println(args)
			_, ok := data.conn.Do("SADD", args...)
			if ok != nil {
				log.Fatal("SADD is wrong.", ok)
			}
			if data.ttl != -1 {
				data.conn.Do("EXPIRE", data.key, data.ttl)
			}
			h.count++
			log.Println("count:", h.count, data.typ, data.key)
		// }(h, data)
		case data := <-h.zsetChan:
			// log.Println(data.typ, data.key, data.ttl)
			// go func(h *Hub, data dataObj) {
			dataMap := data.val.([]interface{})
			// data.conn.Send("MULTI")
			for key, val := range dataMap {
				if key%2 == 0 {
					data.conn.Send("ZADD", data.key, dataMap[key+1], val)
				}
			}
			_, ok := data.conn.Do("")
			if ok != nil {
				log.Fatal("ZADD is wrong.", ok)
			}
			if data.ttl != -1 {
				data.conn.Do("EXPIRE", data.key, data.ttl)
			}
			h.count++
			log.Println("count:", h.count, data.typ, data.key)
			// }(h, data)
		}
	}
}

// redis pool
func newRedisPool(addr string, password string, db int) *redis.Pool {
	return &redis.Pool{
		MaxIdle:     3,
		IdleTimeout: 0,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", addr)
			if err != nil {
				return nil, err
			}

			if _, err := c.Do("AUTH", password); err != nil {
				c.Close()
				return nil, err
			}

			if _, err := c.Do("SELECT", db); err != nil {
				c.Close()
				return nil, err
			}
			return c, nil
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			if time.Since(t) < time.Minute {
				return nil
			}
			_, err := c.Do("PING")
			return err
		},
	}
}
